package cn.zhxu.toys.util;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class SignUtils {

	public static final String DEFAULT_SECRET_KEY = "secret";
	
	public static final String  RSA = "RSA";
	
	public static final String  SHA1WithRSA = "SHA1WithRSA";

	
	/**
	 * 使用默认secretKey生成签名
	 * @param params
	 * @param secretValue
	 * @return
	 */
	public static String genSign(Map<String, ?> params, String secretValue) {
		return doSign(params, DEFAULT_SECRET_KEY, secretValue);
	}
	
	/**
	 * 生成签名
	 * @param params
	 * @param secretKey
	 * @param secretValue
	 * @return
	 */
    public static String genSign(Map<String, ?> params, String secretKey, String secretValue) {
    	return doSign(params, secretKey, secretValue);
    }
    
    /**
     * 密钥不需要键名的签名
     * @param params
     * @param secretValue
     * @return
     */
	public static String signWithoutKey(Map<String, ?> params, String secretValue) {
		return doSign(params, null, secretValue);
	}
	
	
    private static String doSign(Map<String, ?> params, String secretKey, String secretValue) {
    	StringBuilder sb = new StringBuilder(serialize(params));
    	if (secretKey != null) {
    		if (sb.length() > 0) {
        		sb.append("&");
        	}
    		sb.append(secretKey).append("=");
    	}
    	sb.append(secretValue);
        return DigestUtils.toMd5(sb.toString());
    }
    
    /**
     * 按字典顺序序列化MAP
     */
    public static String serialize(Map<String, ?> params) {
    	Map<String, ?> map = params instanceof TreeMap 
    			? params : new TreeMap<>(params);
    	Set<String> keys = map.keySet();
    	boolean first = true;
    	StringBuilder sb = new StringBuilder();
    	for (String key: keys) {
    		Object value = map.get(key);
    		if (value == null) {
    			continue;
    		}
    		String valStr = value.toString();
    		if (StringUtils.notBlank(valStr)) {
    			if (first) {
        			first = false;
        		} else {
        			sb.append("&");
        		}
        		sb.append(key).append("=").append(valStr);
    		}
    	}
    	return sb.toString();
    }
    
    /**
     * 验正RSA签名
     * @param data 待验签的数据
     * @param publicKey Base64格式的公钥
     * @param sign Base64格式的签名
     * @return 验签是否正确
     * @throws GeneralSecurityException 异常
     */
	public static boolean verifyRsaSgin(String data, String publicKey, String sign) throws GeneralSecurityException {
		if (data == null) {
			return false;
		}
		return verifyRsaSgin(data.getBytes(StandardCharsets.UTF_8), publicKey, sign);
	}

    /**
     * 验正RSA签名
     * @param data 待验签的数据
     * @param pubKey Base64格式的公钥
     * @param sign Base64格式的签名
     * @return 验签是否正确
     * @throws GeneralSecurityException 异常
     */
	public static boolean verifyRsaSgin(byte[] data, String pubKey, String sign) throws GeneralSecurityException {
		byte[] keyBytes = Base64.getDecoder().decode(pubKey);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(RSA);
		PublicKey publicKey = keyFactory.generatePublic(keySpec);
		Signature signature = Signature.getInstance(SHA1WithRSA);
		signature.initVerify(publicKey);
		signature.update(data);
		return signature.verify(Base64.getDecoder().decode(sign));
	}
	
    /**
     * RSA签名
     * @param data 待签名数据
     * @param pvtKey 商户私钥
     * @return Base64 格式的签名
     */
    public static String signRsa(byte[] data, String pvtKey) throws GeneralSecurityException {
        byte[] keyBytes = Base64.getDecoder().decode(pvtKey);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey priKey= keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance(SHA1WithRSA);
        signature.initSign(priKey);
        signature.update(data);
        byte[] signBytes = signature.sign();
        return new String(Base64.getEncoder().encode(signBytes));
    }
	
}

